@graphmemory/server 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/api/rest/graph.js +1 -1
  2. package/dist/api/rest/index.js +0 -1
  3. package/dist/api/rest/validation.js +17 -17
  4. package/dist/api/tools/code/get-file-symbols.js +2 -2
  5. package/dist/api/tools/code/get-symbol.js +2 -2
  6. package/dist/api/tools/code/list-files.js +2 -2
  7. package/dist/api/tools/code/search-code.js +2 -1
  8. package/dist/api/tools/code/search-files.js +2 -1
  9. package/dist/api/tools/docs/cross-references.js +2 -1
  10. package/dist/api/tools/docs/explain-symbol.js +2 -1
  11. package/dist/api/tools/docs/find-examples.js +2 -1
  12. package/dist/api/tools/docs/get-node.js +2 -2
  13. package/dist/api/tools/docs/get-toc.js +2 -2
  14. package/dist/api/tools/docs/list-snippets.js +4 -4
  15. package/dist/api/tools/docs/list-topics.js +2 -2
  16. package/dist/api/tools/docs/search-files.js +2 -1
  17. package/dist/api/tools/docs/search-snippets.js +3 -2
  18. package/dist/api/tools/docs/search.js +2 -1
  19. package/dist/api/tools/file-index/get-file-info.js +2 -2
  20. package/dist/api/tools/file-index/list-all-files.js +5 -5
  21. package/dist/api/tools/file-index/search-all-files.js +2 -1
  22. package/dist/api/tools/knowledge/add-attachment.js +26 -4
  23. package/dist/api/tools/knowledge/create-note.js +4 -3
  24. package/dist/api/tools/knowledge/create-relation.js +5 -4
  25. package/dist/api/tools/knowledge/delete-note.js +2 -2
  26. package/dist/api/tools/knowledge/delete-relation.js +4 -3
  27. package/dist/api/tools/knowledge/find-linked-notes.js +4 -3
  28. package/dist/api/tools/knowledge/get-note.js +2 -2
  29. package/dist/api/tools/knowledge/list-notes.js +4 -3
  30. package/dist/api/tools/knowledge/list-relations.js +1 -1
  31. package/dist/api/tools/knowledge/remove-attachment.js +1 -1
  32. package/dist/api/tools/knowledge/search-notes.js +2 -1
  33. package/dist/api/tools/knowledge/update-note.js +6 -5
  34. package/dist/api/tools/skills/add-attachment.js +26 -4
  35. package/dist/api/tools/skills/bump-usage.js +2 -2
  36. package/dist/api/tools/skills/create-skill-link.js +6 -5
  37. package/dist/api/tools/skills/create-skill.js +8 -7
  38. package/dist/api/tools/skills/delete-skill-link.js +4 -3
  39. package/dist/api/tools/skills/delete-skill.js +2 -2
  40. package/dist/api/tools/skills/find-linked-skills.js +4 -3
  41. package/dist/api/tools/skills/get-skill.js +2 -2
  42. package/dist/api/tools/skills/link-skill.js +2 -2
  43. package/dist/api/tools/skills/list-skills.js +4 -3
  44. package/dist/api/tools/skills/recall-skills.js +2 -1
  45. package/dist/api/tools/skills/remove-attachment.js +1 -1
  46. package/dist/api/tools/skills/search-skills.js +2 -1
  47. package/dist/api/tools/skills/update-skill.js +10 -9
  48. package/dist/api/tools/tasks/add-attachment.js +26 -4
  49. package/dist/api/tools/tasks/create-task-link.js +6 -5
  50. package/dist/api/tools/tasks/create-task.js +5 -4
  51. package/dist/api/tools/tasks/delete-task-link.js +4 -3
  52. package/dist/api/tools/tasks/delete-task.js +2 -2
  53. package/dist/api/tools/tasks/find-linked-tasks.js +4 -3
  54. package/dist/api/tools/tasks/get-task.js +2 -2
  55. package/dist/api/tools/tasks/link-task.js +2 -2
  56. package/dist/api/tools/tasks/list-tasks.js +5 -4
  57. package/dist/api/tools/tasks/move-task.js +2 -2
  58. package/dist/api/tools/tasks/remove-attachment.js +1 -1
  59. package/dist/api/tools/tasks/search-tasks.js +2 -1
  60. package/dist/api/tools/tasks/update-task.js +7 -6
  61. package/dist/cli/indexer.js +2 -0
  62. package/dist/graphs/code.js +4 -0
  63. package/dist/graphs/docs.js +4 -0
  64. package/dist/graphs/file-index.js +4 -0
  65. package/dist/graphs/knowledge.js +5 -0
  66. package/dist/graphs/skill.js +5 -0
  67. package/dist/graphs/task.js +5 -0
  68. package/dist/lib/defaults.js +1 -1
  69. package/dist/lib/file-import.js +8 -2
  70. package/dist/lib/file-mirror.js +77 -21
  71. package/dist/lib/graph-persistence.js +42 -0
  72. package/dist/lib/jwt.js +4 -3
  73. package/dist/lib/promise-queue.js +20 -2
  74. package/dist/ui/assets/{NoteForm-SQ0b93i0.js → NoteForm-D0lOYBQq.js} +1 -1
  75. package/dist/ui/assets/{SkillForm-BVsGrNPb.js → SkillForm-CfDWe0Nx.js} +1 -1
  76. package/dist/ui/assets/{TaskForm-DgPVeiI9.js → TaskForm-ltmMCEAE.js} +1 -1
  77. package/dist/ui/assets/{_articleId_-FqdaSeYS.js → _articleId_-mEqH7YfV.js} +1 -1
  78. package/dist/ui/assets/{_docId_-Q0Wmjtp6.js → _docId_-wAt8n8p4.js} +1 -1
  79. package/dist/ui/assets/{_filePath_-BR0gOT_z.js → _filePath_-DQMFMLQh.js} +1 -1
  80. package/dist/ui/assets/{_noteId_-BMWd415J.js → _noteId_-Cqxl6H5q.js} +1 -1
  81. package/dist/ui/assets/{_skillId_-CsHgildJ.js → _skillId_-BlJOfwm_.js} +1 -1
  82. package/dist/ui/assets/{_taskId_-xDHTfbQw.js → _taskId_-Cs8LaIe4.js} +1 -1
  83. package/dist/ui/assets/{_toolName_-BSa2uNSu.js → _toolName_-3CHUDagf.js} +1 -1
  84. package/dist/ui/assets/{attachments-NSvN5_0A.js → attachments-CMDVqPm_.js} +1 -1
  85. package/dist/ui/assets/{docs-iUK8E40J.js → docs-BuFjplSR.js} +1 -1
  86. package/dist/ui/assets/{edit-BzIJy_Oo.js → edit-7NV817UE.js} +1 -1
  87. package/dist/ui/assets/{edit-Dnc067B2.js → edit-Bflx3-cK.js} +1 -1
  88. package/dist/ui/assets/{edit-U_UEI361.js → edit-CdmIaFUI.js} +1 -1
  89. package/dist/ui/assets/{esm-BWiKNcBW.js → esm-CqydI1a6.js} +1 -1
  90. package/dist/ui/assets/{files-B4svJUZh.js → files-BWNbyH1X.js} +1 -1
  91. package/dist/ui/assets/{graph-CcNP1ckP.js → graph-B9nFxoXm.js} +1 -1
  92. package/dist/ui/assets/{help-BJZZtKAR.js → help-CqK0hEmf.js} +1 -1
  93. package/dist/ui/assets/index-80sqSHwS.js +2 -0
  94. package/dist/ui/assets/{knowledge-CV99ToEV.js → knowledge-g4C4l6uL.js} +1 -1
  95. package/dist/ui/assets/{new-Dcx8wlp4.js → new-Bqup97cu.js} +1 -1
  96. package/dist/ui/assets/{new-Sq3NY2oa.js → new-DC3lRvxF.js} +1 -1
  97. package/dist/ui/assets/{new-BypesKiP.js → new-DbsKrGJ4.js} +1 -1
  98. package/dist/ui/assets/{prompts-DbsIe3Pm.js → prompts-DyltFLqJ.js} +1 -1
  99. package/dist/ui/assets/{search-D87r7lIL.js → search-DtRoWsqW.js} +1 -1
  100. package/dist/ui/assets/{skill-BltAsz7M.js → skill-demt31s6.js} +1 -1
  101. package/dist/ui/assets/{skills-Dtmg2kDA.js → skills-DRjYPbZM.js} +1 -1
  102. package/dist/ui/assets/{tasks-BRqIwKCG.js → tasks-CgsSFz6X.js} +1 -1
  103. package/dist/ui/assets/{tools-CM3gQ4TK.js → tools-BDszA6Kh.js} +1 -1
  104. package/dist/ui/assets/{vendor-markdown-CT8ZVEPu.js → vendor-markdown-DngssFHR.js} +27 -27
  105. package/dist/ui/assets/{vendor-md-editor-DmWafJvr.js → vendor-md-editor-DC6xr_29.js} +10 -10
  106. package/dist/ui/assets/{vendor-mui-BPj7d3Sw.js → vendor-mui-DXUYJbRC.js} +1 -1
  107. package/dist/ui/assets/{vendor-mui-icons-B196sG3f.js → vendor-mui-icons-YtgP6dg2.js} +1 -1
  108. package/dist/ui/assets/{vendor-react-CHUjhoxh.js → vendor-react-DxfYAwYK.js} +1 -1
  109. package/dist/ui/index.html +6 -6
  110. package/package.json +1 -1
  111. package/dist/ui/assets/index-CEweXD9O.js +0 -2
@@ -8,7 +8,7 @@ function exportGraph(graph, graphName) {
8
8
  const edges = [];
9
9
  graph.forEachNode((id, attrs) => {
10
10
  // Skip proxy nodes and embeddings for transfer size
11
- const { embedding, fileEmbedding, ...rest } = attrs;
11
+ const { embedding, fileEmbedding, body, pendingImports, pendingEdges, ...rest } = attrs;
12
12
  nodes.push({ id, graph: graphName, ...rest });
13
13
  });
14
14
  graph.forEachEdge((_edge, attrs, source, target) => {
@@ -232,7 +232,6 @@ function createRestApp(projectManager, options) {
232
232
  }
233
233
  return {
234
234
  id,
235
- projectDir: p.config.projectDir,
236
235
  workspaceId: p.workspaceId ?? null,
237
236
  graphs,
238
237
  stats: {
@@ -45,9 +45,9 @@ exports.noteSearchSchema = zod_1.z.object({
45
45
  searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
46
46
  });
47
47
  exports.noteListSchema = zod_1.z.object({
48
- filter: zod_1.z.string().optional(),
49
- tag: zod_1.z.string().optional(),
50
- limit: zod_1.z.coerce.number().int().positive().optional(),
48
+ filter: zod_1.z.string().max(500).optional(),
49
+ tag: zod_1.z.string().max(defaults_1.MAX_TAG_LEN).optional(),
50
+ limit: zod_1.z.coerce.number().int().positive().max(1000).optional(),
51
51
  });
52
52
  // ---------------------------------------------------------------------------
53
53
  // Task schemas
@@ -93,10 +93,10 @@ exports.taskSearchSchema = zod_1.z.object({
93
93
  exports.taskListSchema = zod_1.z.object({
94
94
  status: zod_1.z.enum(['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled']).optional(),
95
95
  priority: zod_1.z.enum(['critical', 'high', 'medium', 'low']).optional(),
96
- tag: zod_1.z.string().optional(),
97
- filter: zod_1.z.string().optional(),
98
- assignee: zod_1.z.string().optional(),
99
- limit: zod_1.z.coerce.number().int().positive().optional(),
96
+ tag: zod_1.z.string().max(defaults_1.MAX_TAG_LEN).optional(),
97
+ filter: zod_1.z.string().max(500).optional(),
98
+ assignee: zod_1.z.string().max(defaults_1.MAX_ASSIGNEE_LEN).optional(),
99
+ limit: zod_1.z.coerce.number().int().positive().max(1000).optional(),
100
100
  });
101
101
  // ---------------------------------------------------------------------------
102
102
  // Search schemas (docs, code, files)
@@ -108,18 +108,18 @@ exports.searchQuerySchema = zod_1.z.object({
108
108
  searchMode: zod_1.z.enum(['hybrid', 'vector', 'keyword']).optional(),
109
109
  });
110
110
  exports.listQuerySchema = zod_1.z.object({
111
- filter: zod_1.z.string().optional(),
112
- limit: zod_1.z.coerce.number().int().positive().optional(),
111
+ filter: zod_1.z.string().max(500).optional(),
112
+ limit: zod_1.z.coerce.number().int().positive().max(1000).optional(),
113
113
  });
114
114
  // ---------------------------------------------------------------------------
115
115
  // File index schemas
116
116
  // ---------------------------------------------------------------------------
117
117
  exports.fileListSchema = zod_1.z.object({
118
- directory: zod_1.z.string().optional(),
119
- extension: zod_1.z.string().optional(),
120
- language: zod_1.z.string().optional(),
121
- filter: zod_1.z.string().optional(),
122
- limit: zod_1.z.coerce.number().int().positive().optional(),
118
+ directory: zod_1.z.string().max(4096).optional(),
119
+ extension: zod_1.z.string().max(100).optional(),
120
+ language: zod_1.z.string().max(100).optional(),
121
+ filter: zod_1.z.string().max(500).optional(),
122
+ limit: zod_1.z.coerce.number().int().positive().max(1000).optional(),
123
123
  });
124
124
  // ---------------------------------------------------------------------------
125
125
  // Graph export schema
@@ -165,9 +165,9 @@ exports.skillSearchSchema = zod_1.z.object({
165
165
  });
166
166
  exports.skillListSchema = zod_1.z.object({
167
167
  source: zod_1.z.enum(['user', 'learned']).optional(),
168
- tag: zod_1.z.string().optional(),
169
- filter: zod_1.z.string().optional(),
170
- limit: zod_1.z.coerce.number().int().positive().optional(),
168
+ tag: zod_1.z.string().max(defaults_1.MAX_TAG_LEN).optional(),
169
+ filter: zod_1.z.string().max(500).optional(),
170
+ limit: zod_1.z.coerce.number().int().positive().max(1000).optional(),
171
171
  });
172
172
  exports.graphExportSchema = zod_1.z.object({
173
173
  scope: zod_1.z.enum(['all', 'docs', 'code', 'knowledge', 'tasks', 'files', 'skills']).default('all'),
@@ -9,12 +9,12 @@ function register(server, mgr) {
9
9
  'Returns an array of { id, kind, name, signature, startLine, endLine, isExported }. ' +
10
10
  'Pass an id to get_symbol to fetch full content including body and docComment.',
11
11
  inputSchema: {
12
- fileId: zod_1.z.string().describe('File path relative to code dir, e.g. "src/lib/graph.ts"'),
12
+ fileId: zod_1.z.string().max(500).describe('File path relative to code dir, e.g. "src/lib/graph.ts"'),
13
13
  },
14
14
  }, async ({ fileId }) => {
15
15
  const symbols = mgr.getFileSymbols(fileId);
16
16
  if (symbols.length === 0) {
17
- return { content: [{ type: 'text', text: `File not found: ${fileId}` }], isError: true };
17
+ return { content: [{ type: 'text', text: 'File not found or contains no symbols' }], isError: true };
18
18
  }
19
19
  const result = symbols.map(s => ({
20
20
  id: s.id,
@@ -9,12 +9,12 @@ function register(server, mgr) {
9
9
  'Node IDs have the form "fileId::symbolName" or "fileId::ClassName::methodName". ' +
10
10
  'Returns id, fileId, kind, name, signature, docComment, body, startLine, endLine, isExported, and crossLinks (notes/tasks linking to this symbol).',
11
11
  inputSchema: {
12
- nodeId: zod_1.z.string().describe('Symbol ID from search_code or get_file_symbols, e.g. "src/lib/graph.ts::updateFile"'),
12
+ nodeId: zod_1.z.string().max(500).describe('Symbol ID from search_code or get_file_symbols, e.g. "src/lib/graph.ts::updateFile"'),
13
13
  },
14
14
  }, async ({ nodeId }) => {
15
15
  const symbol = mgr.getSymbol(nodeId);
16
16
  if (!symbol) {
17
- return { content: [{ type: 'text', text: `Symbol not found: ${nodeId}` }], isError: true };
17
+ return { content: [{ type: 'text', text: 'Symbol not found' }], isError: true };
18
18
  }
19
19
  const { embedding: _embedding, mtime: _mtime, ...rest } = symbol;
20
20
  return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
@@ -9,8 +9,8 @@ function register(server, mgr) {
9
9
  'Returns an array of { fileId, symbolCount }. ' +
10
10
  'Pass a fileId to get_file_symbols to see all its declarations.',
11
11
  inputSchema: {
12
- filter: zod_1.z.string().optional().describe('Case-insensitive substring to match against file paths, e.g. "graph" or "src/lib"'),
13
- limit: zod_1.z.number().optional().describe('Maximum number of results to return (default 20)'),
12
+ filter: zod_1.z.string().max(500).optional().describe('Case-insensitive substring to match against file paths, e.g. "graph" or "src/lib"'),
13
+ limit: zod_1.z.number().max(1000).optional().describe('Maximum number of results to return (default 20)'),
14
14
  },
15
15
  }, async ({ filter, limit }) => ({
16
16
  content: [{ type: 'text', text: JSON.stringify(mgr.listFiles(filter, limit), null, 2) }],
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('search_code', {
7
8
  description: 'Semantic search over the indexed source code. ' +
@@ -14,7 +15,7 @@ function register(server, mgr) {
14
15
  'id, fileId, kind, name, signature, docComment, startLine, endLine, score. ' +
15
16
  'Set includeBody=true to include full source code in results (avoids extra get_symbol calls).',
16
17
  inputSchema: {
17
- query: zod_1.z.string().describe('Natural language or code search query, e.g. "function that loads the graph from disk"'),
18
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language or code search query, e.g. "function that loads the graph from disk"'),
18
19
  topK: zod_1.z.number().min(1).max(500).optional().describe('How many top similar symbols to use as seeds (default 5)'),
19
20
  bfsDepth: zod_1.z.number().min(0).max(10).optional().describe('How many hops to follow graph edges from each seed (default 1; 0 = no expansion)'),
20
21
  maxResults: zod_1.z.number().min(1).max(500).optional().describe('Maximum number of results to return (default 20)'),
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('search_files', {
7
8
  description: 'Semantic search over indexed source code files. ' +
@@ -11,7 +12,7 @@ function register(server, mgr) {
11
12
  'fileId, symbolCount, score. ' +
12
13
  'Use this to discover which source files are relevant before diving into symbols with get_file_symbols or search_code.',
13
14
  inputSchema: {
14
- query: zod_1.z.string().describe('Natural language or path search query, e.g. "graph persistence" or "search module"'),
15
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language or path search query, e.g. "graph persistence" or "search module"'),
15
16
  topK: zod_1.z.number().min(1).max(500).optional().describe('Maximum number of results to return (default 10)'),
16
17
  minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.3)'),
17
18
  },
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, docMgr, codeMgr) {
6
7
  const docGraph = docMgr.graph;
7
8
  const codeGraph = codeMgr.graph;
@@ -12,7 +13,7 @@ function register(server, docMgr, codeMgr) {
12
13
  'and examples (code blocks in docs that contain the symbol). ' +
13
14
  'This is the most comprehensive way to understand a symbol — combining source code, docs, and examples.',
14
15
  inputSchema: {
15
- symbol: zod_1.z.string().describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
16
+ symbol: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
16
17
  },
17
18
  }, async ({ symbol }) => {
18
19
  // 1. Search CodeGraph for definitions
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  const graph = mgr.graph;
7
8
  server.registerTool('explain_symbol', {
@@ -10,7 +11,7 @@ function register(server, mgr) {
10
11
  'and the surrounding text section that provides context/explanation. ' +
11
12
  'Use this to understand what a function, class, or type does based on documentation.',
12
13
  inputSchema: {
13
- symbol: zod_1.z.string().describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
14
+ symbol: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Symbol name to look up, e.g. "createUser", "AuthService"'),
14
15
  limit: zod_1.z.number().optional().describe('Max results to return (default 10)'),
15
16
  },
16
17
  }, async ({ symbol, limit = 10 }) => {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  const graph = mgr.graph;
7
8
  server.registerTool('find_examples', {
@@ -10,7 +11,7 @@ function register(server, mgr) {
10
11
  'Use this to find usage examples of a known symbol in the docs. ' +
11
12
  'Returns matching code block nodes with id, fileId, language, symbols, content, and the parent section context.',
12
13
  inputSchema: {
13
- symbol: zod_1.z.string().describe('Symbol name to search for, e.g. "createUser", "UserService"'),
14
+ symbol: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Symbol name to search for, e.g. "createUser", "UserService"'),
14
15
  limit: zod_1.z.number().optional().describe('Max results to return (default 20)'),
15
16
  },
16
17
  }, async ({ symbol, limit = 20 }) => {
@@ -11,12 +11,12 @@ function register(server, mgr) {
11
11
  '"docs/auth.md::Overview" for a named section. ' +
12
12
  'Returns id, fileId, title, content, level, links, mtime, and crossLinks (notes/tasks linking to this node).',
13
13
  inputSchema: {
14
- nodeId: zod_1.z.string().describe('Node ID from search results or get_toc, e.g. "docs/auth.md" or "docs/auth.md::Overview"'),
14
+ nodeId: zod_1.z.string().max(500).describe('Node ID from search results or get_toc, e.g. "docs/auth.md" or "docs/auth.md::Overview"'),
15
15
  },
16
16
  }, async ({ nodeId }) => {
17
17
  const node = mgr.getNode(nodeId);
18
18
  if (!node) {
19
- return { content: [{ type: 'text', text: `Node not found: ${nodeId}` }], isError: true };
19
+ return { content: [{ type: 'text', text: 'Node not found' }], isError: true };
20
20
  }
21
21
  const { embedding: _embedding, ...rest } = node;
22
22
  return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
@@ -9,12 +9,12 @@ function register(server, mgr) {
9
9
  'Returns an array of { id, title, level } objects. ' +
10
10
  'The id field is a node ID you can pass directly to get_node to fetch full content.',
11
11
  inputSchema: {
12
- fileId: zod_1.z.string().describe('File path relative to docs dir, e.g. "docs/auth.md"'),
12
+ fileId: zod_1.z.string().max(500).describe('File path relative to docs dir, e.g. "docs/auth.md"'),
13
13
  },
14
14
  }, async ({ fileId }) => {
15
15
  const chunks = mgr.getFileChunks(fileId);
16
16
  if (chunks.length === 0) {
17
- return { content: [{ type: 'text', text: `File not found: ${fileId}` }], isError: true };
17
+ return { content: [{ type: 'text', text: 'File not found' }], isError: true };
18
18
  }
19
19
  const toc = chunks.map(c => ({ id: c.id, title: c.title, level: c.level }));
20
20
  return { content: [{ type: 'text', text: JSON.stringify(toc, null, 2) }] };
@@ -10,10 +10,10 @@ function register(server, mgr) {
10
10
  'Supports filtering by file, language, and content substring. ' +
11
11
  'Use this to discover what code examples exist in the docs.',
12
12
  inputSchema: {
13
- fileId: zod_1.z.string().optional().describe('Filter by file, e.g. "docs/auth.md"'),
14
- filter: zod_1.z.string().optional().describe('Case-insensitive substring match on content'),
15
- language: zod_1.z.string().optional().describe('Filter by language, e.g. "typescript"'),
16
- limit: zod_1.z.number().optional().describe('Max results to return (default 20)'),
13
+ fileId: zod_1.z.string().max(500).optional().describe('Filter by file, e.g. "docs/auth.md"'),
14
+ filter: zod_1.z.string().max(500).optional().describe('Case-insensitive substring match on content'),
15
+ language: zod_1.z.string().max(100).optional().describe('Filter by language, e.g. "typescript"'),
16
+ limit: zod_1.z.number().max(1000).optional().describe('Max results to return (default 20)'),
17
17
  },
18
18
  }, async ({ fileId, filter, language, limit = 20 }) => {
19
19
  const lowerFilter = filter?.toLowerCase();
@@ -9,8 +9,8 @@ function register(server, mgr) {
9
9
  'Returns an array of { fileId, title, chunks }. ' +
10
10
  'Pass a fileId to get_toc to see its structure, or to search to query it.',
11
11
  inputSchema: {
12
- filter: zod_1.z.string().optional().describe('Case-insensitive substring to match against file paths, e.g. "auth" or "api"'),
13
- limit: zod_1.z.number().optional().describe('Maximum number of results to return (default 20)'),
12
+ filter: zod_1.z.string().max(500).optional().describe('Case-insensitive substring to match against file paths, e.g. "auth" or "api"'),
13
+ limit: zod_1.z.number().max(1000).optional().describe('Maximum number of results to return (default 20)'),
14
14
  },
15
15
  }, async ({ filter, limit }) => ({
16
16
  content: [{ type: 'text', text: JSON.stringify(mgr.listFiles(filter, limit), null, 2) }],
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('search_topic_files', {
7
8
  description: 'Semantic search over indexed documentation files. ' +
@@ -11,7 +12,7 @@ function register(server, mgr) {
11
12
  'fileId, title, chunks, score. ' +
12
13
  'Use this to discover which doc files are relevant before diving into content with search or get_toc.',
13
14
  inputSchema: {
14
- query: zod_1.z.string().describe('Natural language search query, e.g. "authentication setup" or "API endpoints"'),
15
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language search query, e.g. "authentication setup" or "API endpoints"'),
15
16
  topK: zod_1.z.number().min(1).max(500).optional().describe('Maximum number of results to return (default 10)'),
16
17
  minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.3)'),
17
18
  },
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  const graph = mgr.graph;
7
8
  server.registerTool('search_snippets', {
@@ -11,10 +12,10 @@ function register(server, mgr) {
11
12
  'Use this when looking for code examples by description, e.g. "authentication example" or "database query". ' +
12
13
  'Returns code block nodes sorted by relevance score.',
13
14
  inputSchema: {
14
- query: zod_1.z.string().describe('Natural language search query'),
15
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language search query'),
15
16
  topK: zod_1.z.number().min(1).max(500).optional().describe('Max results to return (default 10)'),
16
17
  minScore: zod_1.z.number().min(0).max(1).optional().describe('Minimum relevance score 0–1 (default 0.3)'),
17
- language: zod_1.z.string().optional().describe('Filter by language, e.g. "typescript", "python"'),
18
+ language: zod_1.z.string().max(100).optional().describe('Filter by language, e.g. "typescript", "python"'),
18
19
  },
19
20
  }, async ({ query, topK = 10, minScore = 0.3, language }) => {
20
21
  // Search all nodes via mgr.search (no BFS expansion), then filter to code blocks only
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('search', {
7
8
  description: 'Semantic search over the indexed documentation. ' +
@@ -13,7 +14,7 @@ function register(server, mgr) {
13
14
  'Use the id from results to fetch full content with get_node. ' +
14
15
  'Prefer this tool when looking for information without knowing which file contains it.',
15
16
  inputSchema: {
16
- query: zod_1.z.string().describe('Natural language search query'),
17
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Natural language search query'),
17
18
  topK: zod_1.z.number().min(1).max(500).optional().describe('How many top similar sections to use as seeds for graph expansion (default 5)'),
18
19
  bfsDepth: zod_1.z.number().min(0).max(10).optional().describe('How many hops to follow cross-document links from each seed (default 1; 0 = no expansion)'),
19
20
  maxResults: zod_1.z.number().min(1).max(500).optional().describe('Maximum number of results to return (default 20)'),
@@ -9,12 +9,12 @@ function register(server, mgr) {
9
9
  'For directories: returns filePath, kind, fileName, directory, fileCount, size (total of direct children). ' +
10
10
  'Use "." for the project root.',
11
11
  inputSchema: {
12
- filePath: zod_1.z.string().describe('Relative file or directory path (e.g. "src/lib/config.ts" or "src/lib")'),
12
+ filePath: zod_1.z.string().max(4096).describe('Relative file or directory path (e.g. "src/lib/config.ts" or "src/lib")'),
13
13
  },
14
14
  }, async ({ filePath }) => {
15
15
  const info = mgr.getFileInfo(filePath);
16
16
  if (!info) {
17
- return { content: [{ type: 'text', text: `File or directory not found: ${filePath}` }], isError: true };
17
+ return { content: [{ type: 'text', text: 'File or directory not found' }], isError: true };
18
18
  }
19
19
  return { content: [{ type: 'text', text: JSON.stringify(info, null, 2) }] };
20
20
  });
@@ -10,15 +10,15 @@ function register(server, mgr) {
10
10
  'Returns an array of { filePath, kind, fileName, extension, language, mimeType, size, fileCount }. ' +
11
11
  'Use search_all_files for semantic search or get_file_info for detailed metadata on a specific path.',
12
12
  inputSchema: {
13
- directory: zod_1.z.string().optional()
13
+ directory: zod_1.z.string().max(4096).optional()
14
14
  .describe('List immediate children of this directory (e.g. ".", "src/lib"). Default: lists all files'),
15
- extension: zod_1.z.string().optional()
15
+ extension: zod_1.z.string().max(100).optional()
16
16
  .describe('Filter by extension (e.g. ".ts", ".md", ".png")'),
17
- language: zod_1.z.string().optional()
17
+ language: zod_1.z.string().max(100).optional()
18
18
  .describe('Filter by language (e.g. "typescript", "markdown", "json")'),
19
- filter: zod_1.z.string().optional()
19
+ filter: zod_1.z.string().max(500).optional()
20
20
  .describe('Substring filter on file path (case-insensitive)'),
21
- limit: zod_1.z.number().optional().default(50)
21
+ limit: zod_1.z.number().max(1000).optional().default(50)
22
22
  .describe('Max results (default 50)'),
23
23
  },
24
24
  }, async ({ directory, extension, language, filter, limit }) => {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('search_all_files', {
7
8
  description: 'Semantic search over all indexed project files by file path. ' +
@@ -11,7 +12,7 @@ function register(server, mgr) {
11
12
  'filePath, fileName, extension, language, size, score. ' +
12
13
  'Use this to discover which project files are relevant to a topic.',
13
14
  inputSchema: {
14
- query: zod_1.z.string().describe('Search query'),
15
+ query: zod_1.z.string().max(defaults_1.MAX_SEARCH_QUERY_LEN).describe('Search query'),
15
16
  topK: zod_1.z.number().min(1).max(500).optional().default(10)
16
17
  .describe('Max results (default 10)'),
17
18
  minScore: zod_1.z.number().optional().default(0.3)
@@ -14,14 +14,36 @@ function register(server, mgr) {
14
14
  'The file is copied into the note directory (.notes/{noteId}/). ' +
15
15
  'Returns attachment metadata (filename, mimeType, size).',
16
16
  inputSchema: {
17
- noteId: zod_1.z.string().describe('ID of the note to attach the file to'),
18
- filePath: zod_1.z.string().describe('Absolute path to the file on disk'),
17
+ noteId: zod_1.z.string().max(500).describe('ID of the note to attach the file to'),
18
+ filePath: zod_1.z.string().max(4096).describe('Absolute path to the file on disk'),
19
19
  },
20
20
  }, async ({ noteId, filePath }) => {
21
21
  const resolved = path_1.default.resolve(filePath);
22
+ const projectDir = mgr.projectDir;
23
+ if (!projectDir) {
24
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'No project directory configured' }) }], isError: true };
25
+ }
26
+ let realResolved;
27
+ try {
28
+ realResolved = fs_1.default.realpathSync(resolved);
29
+ }
30
+ catch {
31
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'File not found' }) }], isError: true };
32
+ }
33
+ let realProject;
34
+ try {
35
+ realProject = fs_1.default.realpathSync(path_1.default.resolve(projectDir));
36
+ }
37
+ catch {
38
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'Project directory not found' }) }], isError: true };
39
+ }
40
+ const normalizedProject = realProject + path_1.default.sep;
41
+ if (!realResolved.startsWith(normalizedProject) && realResolved !== realProject) {
42
+ return { content: [{ type: 'text', text: JSON.stringify({ error: 'File path must be within the project directory' }) }], isError: true };
43
+ }
22
44
  let stat;
23
45
  try {
24
- stat = fs_1.default.statSync(resolved);
46
+ stat = fs_1.default.statSync(realResolved);
25
47
  }
26
48
  catch {
27
49
  return { content: [{ type: 'text', text: JSON.stringify({ error: 'File not found' }) }], isError: true };
@@ -32,7 +54,7 @@ function register(server, mgr) {
32
54
  if (stat.size > defaults_1.MAX_UPLOAD_SIZE) {
33
55
  return { content: [{ type: 'text', text: JSON.stringify({ error: 'File exceeds 50 MB limit' }) }], isError: true };
34
56
  }
35
- const data = fs_1.default.readFileSync(resolved);
57
+ const data = fs_1.default.readFileSync(realResolved);
36
58
  const filename = path_1.default.basename(resolved);
37
59
  const meta = mgr.addAttachment(noteId, filename, data);
38
60
  if (!meta) {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('create_note', {
7
8
  description: 'Create a new note or fact in the knowledge graph. ' +
@@ -9,9 +10,9 @@ function register(server, mgr) {
9
10
  'Returns the generated noteId (slug from title). ' +
10
11
  'Use create_relation to link notes together.',
11
12
  inputSchema: {
12
- title: zod_1.z.string().describe('Short title for the note, e.g. "Auth uses JWT tokens"'),
13
- content: zod_1.z.string().describe('Full text content of the note'),
14
- tags: zod_1.z.array(zod_1.z.string()).optional().describe('Optional tags for filtering, e.g. ["architecture", "decision"]'),
13
+ title: zod_1.z.string().max(defaults_1.MAX_TITLE_LEN).describe('Short title for the note, e.g. "Auth uses JWT tokens"'),
14
+ content: zod_1.z.string().max(defaults_1.MAX_NOTE_CONTENT_LEN).describe('Full text content of the note'),
15
+ tags: zod_1.z.array(zod_1.z.string().max(defaults_1.MAX_TAG_LEN)).max(defaults_1.MAX_TAGS_COUNT).optional().describe('Optional tags for filtering, e.g. ["architecture", "decision"]'),
15
16
  },
16
17
  }, async ({ title, content, tags }) => {
17
18
  const noteId = await mgr.createNote(title, content, tags ?? []);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('create_relation', {
7
8
  description: 'Create a directed relation from a note to another note or to a node in the docs/code/files graph. ' +
@@ -9,12 +10,12 @@ function register(server, mgr) {
9
10
  'e.g. "relates_to", "depends_on", "contradicts", "supports", "part_of", "references". ' +
10
11
  'Set targetGraph to "docs", "code", "files", or "tasks" to link to a doc chunk, code symbol, file/directory, or task.',
11
12
  inputSchema: {
12
- fromId: zod_1.z.string().describe('Source note ID'),
13
- toId: zod_1.z.string().describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
14
- kind: zod_1.z.string().describe('Relation type, e.g. "depends_on", "references"'),
13
+ fromId: zod_1.z.string().max(500).describe('Source note ID'),
14
+ toId: zod_1.z.string().max(500).describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
15
+ kind: zod_1.z.string().max(defaults_1.MAX_LINK_KIND_LEN).describe('Relation type, e.g. "depends_on", "references"'),
15
16
  targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks', 'skills']).optional()
16
17
  .describe('Set to "docs", "code", "files", "tasks", or "skills" to create a cross-graph link instead of note-to-note'),
17
- projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
18
+ projectId: zod_1.z.string().max(defaults_1.MAX_PROJECT_ID_LEN).optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
18
19
  },
19
20
  }, async ({ fromId, toId, kind, targetGraph, projectId }) => {
20
21
  const created = mgr.createRelation(fromId, toId, kind, targetGraph, projectId);
@@ -7,12 +7,12 @@ function register(server, mgr) {
7
7
  description: 'Delete a note from the knowledge graph. ' +
8
8
  'Also removes all relations (edges) connected to this note.',
9
9
  inputSchema: {
10
- noteId: zod_1.z.string().describe('ID of the note to delete'),
10
+ noteId: zod_1.z.string().max(500).describe('ID of the note to delete'),
11
11
  },
12
12
  }, async ({ noteId }) => {
13
13
  const deleted = mgr.deleteNote(noteId);
14
14
  if (!deleted) {
15
- return { content: [{ type: 'text', text: `Note not found: ${noteId}` }], isError: true };
15
+ return { content: [{ type: 'text', text: 'Note not found' }], isError: true };
16
16
  }
17
17
  return { content: [{ type: 'text', text: JSON.stringify({ noteId, deleted: true }, null, 2) }] };
18
18
  });
@@ -2,16 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('delete_relation', {
7
8
  description: 'Delete a directed relation between a note and another note or a cross-graph target. ' +
8
9
  'Set targetGraph to "docs", "code", "files", or "tasks" when deleting a cross-graph link.',
9
10
  inputSchema: {
10
- fromId: zod_1.z.string().describe('Source note ID'),
11
- toId: zod_1.z.string().describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
11
+ fromId: zod_1.z.string().max(500).describe('Source note ID'),
12
+ toId: zod_1.z.string().max(500).describe('Target note ID, or target node ID in docs/code/files/tasks graph'),
12
13
  targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks', 'skills']).optional()
13
14
  .describe('Set to "docs", "code", "files", "tasks", or "skills" when deleting a cross-graph link'),
14
- projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
15
+ projectId: zod_1.z.string().max(defaults_1.MAX_PROJECT_ID_LEN).optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
15
16
  },
16
17
  }, async ({ fromId, toId, targetGraph, projectId }) => {
17
18
  const deleted = mgr.deleteRelation(fromId, toId, targetGraph, projectId);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('find_linked_notes', {
7
8
  description: 'Find all notes in the knowledge graph that link to a specific node in the docs, code, files, or tasks graph. ' +
@@ -10,10 +11,10 @@ function register(server, mgr) {
10
11
  'Returns an array of { noteId, title, kind, tags }. ' +
11
12
  'Use get_note to fetch full content of a returned note.',
12
13
  inputSchema: {
13
- targetId: zod_1.z.string().describe('Target node ID in the external graph (e.g. "src/config.ts", "src/auth.ts::login", "docs/api.md::Setup")'),
14
+ targetId: zod_1.z.string().max(defaults_1.MAX_TARGET_NODE_ID_LEN).describe('Target node ID in the external graph (e.g. "src/config.ts", "src/auth.ts::login", "docs/api.md::Setup")'),
14
15
  targetGraph: zod_1.z.enum(['docs', 'code', 'files', 'tasks', 'skills']).describe('Which graph the target belongs to'),
15
- kind: zod_1.z.string().optional().describe('Filter by relation kind (e.g. "references", "depends_on"). If omitted, returns all relations.'),
16
- projectId: zod_1.z.string().optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
16
+ kind: zod_1.z.string().max(defaults_1.MAX_LINK_KIND_LEN).optional().describe('Filter by relation kind (e.g. "references", "depends_on"). If omitted, returns all relations.'),
17
+ projectId: zod_1.z.string().max(defaults_1.MAX_PROJECT_ID_LEN).optional().describe('Project ID that the target node belongs to. Defaults to the current project.'),
17
18
  },
18
19
  }, async ({ targetId, targetGraph, kind, projectId }) => {
19
20
  const results = mgr.findLinkedNotes(targetGraph, targetId, kind, projectId);
@@ -7,12 +7,12 @@ function register(server, mgr) {
7
7
  description: 'Return the full content of a note by its ID. ' +
8
8
  'Returns id, title, content, tags, createdAt, updatedAt, and relations (including cross-graph links from/to tasks, docs, code, files).',
9
9
  inputSchema: {
10
- noteId: zod_1.z.string().describe('Note ID, e.g. "auth-uses-jwt-tokens"'),
10
+ noteId: zod_1.z.string().max(500).describe('Note ID, e.g. "auth-uses-jwt-tokens"'),
11
11
  },
12
12
  }, async ({ noteId }) => {
13
13
  const note = mgr.getNote(noteId);
14
14
  if (!note) {
15
- return { content: [{ type: 'text', text: `Note not found: ${noteId}` }], isError: true };
15
+ return { content: [{ type: 'text', text: 'Note not found' }], isError: true };
16
16
  }
17
17
  const { embedding: _embedding, ...rest } = note;
18
18
  return { content: [{ type: 'text', text: JSON.stringify(rest, null, 2) }] };
@@ -2,15 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.register = register;
4
4
  const zod_1 = require("zod");
5
+ const defaults_1 = require("../../../lib/defaults");
5
6
  function register(server, mgr) {
6
7
  server.registerTool('list_notes', {
7
8
  description: 'List notes in the knowledge graph. ' +
8
9
  'Optionally filter by title/id substring and/or tag. ' +
9
10
  'Returns an array of { id, title, tags, updatedAt } sorted by most recently updated.',
10
11
  inputSchema: {
11
- filter: zod_1.z.string().optional().describe('Case-insensitive substring to match against note title or ID'),
12
- tag: zod_1.z.string().optional().describe('Filter by tag (exact match, case-insensitive)'),
13
- limit: zod_1.z.number().optional().describe('Maximum number of results (default 20)'),
12
+ filter: zod_1.z.string().max(500).optional().describe('Case-insensitive substring to match against note title or ID'),
13
+ tag: zod_1.z.string().max(defaults_1.MAX_TAG_LEN).optional().describe('Filter by tag (exact match, case-insensitive)'),
14
+ limit: zod_1.z.number().max(1000).optional().describe('Maximum number of results (default 20)'),
14
15
  },
15
16
  }, async ({ filter, tag, limit }) => ({
16
17
  content: [{ type: 'text', text: JSON.stringify(mgr.listNotes(filter, tag, limit), null, 2) }],
@@ -8,7 +8,7 @@ function register(server, mgr) {
8
8
  'Returns an array of { fromId, toId, kind, targetGraph? }. ' +
9
9
  'Cross-graph links include targetGraph ("docs", "code", "files", or "tasks") and resolve the real node ID.',
10
10
  inputSchema: {
11
- noteId: zod_1.z.string().describe('Note ID to list relations for'),
11
+ noteId: zod_1.z.string().max(500).describe('Note ID to list relations for'),
12
12
  },
13
13
  }, async ({ noteId }) => {
14
14
  const relations = mgr.listRelations(noteId);
@@ -6,7 +6,7 @@ function register(server, mgr) {
6
6
  server.registerTool('remove_note_attachment', {
7
7
  description: 'Remove an attachment from a note. The file is deleted from disk.',
8
8
  inputSchema: {
9
- noteId: zod_1.z.string().describe('ID of the note'),
9
+ noteId: zod_1.z.string().max(500).describe('ID of the note'),
10
10
  filename: zod_1.z.string().min(1).max(255)
11
11
  .refine(s => !/[/\\]/.test(s), 'Filename must not contain path separators')
12
12
  .refine(s => !s.includes('..'), 'Filename must not contain ..')